package demo3


// 抽象工厂 abstractfactory OrderMainDAO  OrderDetailDAO  DAOFactory RDBMainDAO
// 抽象工厂模式用于生成产品族的工厂，所生成的对象是有关联的。
// 如果抽象工厂退化成生成的对象无关联则成为工厂函数模式。
// 比如本例子中使用RDB和XML存储订单信息，抽象工厂分别能生成相关的主订单信息和订单详情信息。
// 如果业务逻辑中需要替换使用的时候只需要改动工厂函数相关的类就能替换使用不同的存储方式了。


// demo1 分析::
// 通过工厂生产不同的额实例，不同的实例拥有不同的实现，这里生产的不是具体的实例而是一类实例，也就是抽象的对象，
// 这样做的好处就是让工厂生产的时候能够生产一类，而不是具体的某一个，所以工厂要理解工厂实质的含义，提现工厂的作用
// 工厂生产 抽抽象的对象，对应抽象对象都拥有自己对象对应的方法，也许也有不同的属性和方法
import "fmt"

type OrderMainDAO interface{
	// 订单
	SaveOrderMain()
}
type OrderDetailDAO interface{
	// 订单详情
	SaveOrderMain()
}

// 订单工厂
type DAOFactory interface{ 
	CreateOrderMainDAO() OrderMainDAO // 订单对象
	CreateOrderDetailDAO() OrderDetailDAO // 订单详情
}

// 对象的方法
type RDBMainDAO struct{}
func (*RDBMainDAO)SaveOrderMain(){
	fmt.Println("rdb-main-save")
}
// 对象的方法
type RDBDetailDAO struct{}
func (*RDBDetailDAO)SaveOrderMain(){
	fmt.Println("rdb-detail-save")
}

// 生产对象
type RDBFactory struct{}
func (*RDBFactory)CreateOrderMainDAO() OrderMainDAO{
	return &RDBMainDAO{}
}
func (*RDBFactory)CreateOrderDetailDAO() OrderDetailDAO{
	return &RDBDetailDAO{}
}

// --------------------------------场景2-----------------------------------

// 需求: 现有一个汽车相关加工厂，生产不同的汽车同时也生产飞机，每一个汽车都用于Run 的方法实现，飞机不仅仅拥有run 同时拥有fly，
// 对比工厂方法不同的实现:工厂的方法实现，是工厂生产不同的操作员，不同的操作员可以生产不同的对象，如果原有的操作员满足不了现有工厂
// 功能的时候，比如工厂添加新功能的时候，这个时候可以通过继承来实现新功能的开发，
// 该抽象功能是在工厂的基础上做了一层抽象的操作，也就是在工厂方法的上层的实现

// 抽象层
type Car interface{
	// 汽车对象
	Run()
}

type Plane interface{
	// 飞机对象
	Run()
	Fly() // 特有的方法
}

// 军事基地
type MilitaryFactory interface{
	CreateCar() Car // 制造某一类的实例
	CreatePlane() Plane // 同上
}

// 实现层
// 该层简单说可以是对应对象的逻辑实现层
type BenChiCar struct{} 
func (*BenChiCar)Run(){
	fmt.Println("car---running")
}
type RedPlane struct{}
func (*RedPlane)Run(){
	fmt.Println("plane---running")
}
func (*RedPlane)Fly(){
	fmt.Println("fly---sky")
}

// 工厂实现，对外暴露
type DoFactory struct{}

func (*DoFactory)CreateCar() Car{
	// 这里可以调整，通过不同的参数生产不同类
	return  &BenChiCar{}
}
func (*DoFactory)CreatePlane() Plane{
	return &RedPlane{}
}

// --------------------------------场景3-----------------------------------

// 需求： 工厂生产不同的操作员，不同的操作员拥有自己的方法，
// 不同的操作员负责生产不同的零食，简单的说不同的流水线上操作员工作不同，
// DDD: 分析===> 多个操作员 || 操作不同(领域服务) 
// OperatorFactory 操作员生产工厂， Operator 操作员[”辣条系列“，”坚果系列“，”干果系列“,...],
// 拓展: 随着业务增加市场需求，工厂开始生产复合零食大礼包 辣条和坚果组合大礼包促销
// 拓展2: 随着业务增加市场需求，工厂开始生产牛奶，需要新的技术人员来搞，但是这个人同样也要有该方面的经验


// 抽象层
// Operator 操作员  OperatorFactory 工厂招聘  SpicyStripSeries辣条类 NutSeries坚果类 DriedFruitSeries干果类
// 食品都能吃 Eat() || 香辣酸甜方法暂时就不拓展了


// 虽然都用于同样的方法，但是类别不一样，这里一定要明白工厂的含义，这里可能会有人问道为什么不用继承 ，这个根据场景需求来的没有规定
// 食物
type SpicyStripSeries interface{ 
	Context()
	Eat()
}
type NutSeries interface{
	Context()
	Eat()
}
type DriedFruitSeries interface{
	Context()
	Eat()
}
// 工人 || 工人可以操作这么多东西，就不做工人细分了，工人细分的话，就慢慢发现工厂的缺点了，
// 细分会导致工厂变得庞大，不好维护，同时业务慢慢变的复杂，这个就是缺点
type Operator interface{
	// 这里有个细节必须注意，关于方法实现的问题， 定义的方法以及逻辑业务拓展的方法这里必须全部实现，
	CreateSpicy() SpicyStripSeries
	CreateNut() NutSeries
	CreateDried() DriedFruitSeries
	// 工人属性
	Set(name string)
	Get() string
	// 查询工人的信息和业绩
	GetResult()

	// 大礼包
	FoodCombination(name string)
	// 新增业务
	foodMilk()
}

// 工厂 招聘工人
type SnacksFactory interface{
	CreateOperator() Operator
}

// 实现层
// 具体食物
type foodSpicy struct{}
func (*foodSpicy) Context() {fmt.Println("辣条")}
func (*foodSpicy) Eat(){
	fmt.Println("吃辣条.....")
}
type foodNut struct{}
func (*foodNut) Context() {fmt.Println("坚果")}
func (*foodNut)Eat(){
	fmt.Println("吃坚果....")
}
type foodDried struct{}
func (*foodDried) Context() {fmt.Println("干果")}
func (*foodDried)Eat(){
	fmt.Println("吃干果。。。")
}

// 某个操作员 
// 只要拓展新业务那么都要完全实现
// 随着业务的拓展，实现的方法会越来越多
type foodOperator struct{
	name string
}

func (o *foodOperator)Set(name string){
	o.name=name
}
func (o *foodOperator)Get() string{
	return o.name
}
func (*foodOperator) CreateSpicy() SpicyStripSeries{
	return &foodSpicy{}
}
func (*foodOperator) CreateNut() NutSeries{
	return &foodNut{}
}
func (*foodOperator) CreateDried() DriedFruitSeries{
	return &foodDried{}
}

func (*foodOperator)foodMilk(){}

func (o *foodOperator) GetResult(){
	// 可以是打印结果||也可以是一个返回值
	fmt.Println(o.name)
	fmt.Println("----------------------")

}

// // 选项设计 搭配

// type Options struct{
// 	// 参数定义规则地方
// 	StrOption1 string
// 	StrOption2 string
// 	StrOption3 string
// }

// type optionInterface interface{
// 	OptionInit(...OptionFunc) *Options
// }

// type OptionFunc func(*Options)

// func WithOption1(opt string) OptionFunc{
// 	return func(o *Options){
// 		o.StrOption1 = opt
// 	}
// }
// func WithOption2(opt string) OptionFunc{
// 	return func(o *Options){
// 		o.StrOption2 = opt
// 	}
// }
// func WithOption3(opt string) OptionFunc{
// 	return func(o *Options){
// 		o.StrOption3 = opt
// 	}
// }

// func OptionInit(opts ...OptionFunc) *Options{
// 	var o Options
// 	for _,opt := range opts{
// 		opt(&o)
// 	}
// 	return &o
// }




type FoodFactory struct{}
func (*FoodFactory) CreateOperator(name string) Operator{
	// 根据传入的参数，来判断调用的员工 
	// 参数的传入也可以灵活配置，比如搭配选项设计模式会更好
	switch name {
	case "高级员工":
		return &MilkOperator{
			foodOperator:&foodOperator{},
		}
	default:
		return &foodOperator{}
	}
}

// 拓展1 大礼包 组合

func (o *foodOperator)FoodCombination(name string){
	switch name {
	case "辣类":
		fmt.Println("恭喜你获取到大礼包为...:")
		o.CreateSpicy().Context()
	case "果类":
		fmt.Println("恭喜你获取到大礼包为...:")
		o.CreateNut().Context()
		o.CreateDried().Context()
	default:
		fmt.Println("谢谢惠顾")
	}
}

// 拓展2 新业务添加  当然也可以重新写一个抽象员工 和 抽象员工的实现，

type MilkOperator struct{
	*foodOperator
}

func (o *MilkOperator)foodMilk(){
	fmt.Println(o.name ,"生产牛奶")
}

func (o *MilkOperator) GetResult(){
	// 可以是打印结果||也可以是一个返回值
	fmt.Println(o.name,"获得零食如下")
	o.CreateSpicy().Context()
	o.CreateNut().Context() 
	o.CreateDried().Context()
	fmt.Println("----------------------")
}


// --------------------------------场景4-----------------------------------

// 需求分析，关于状态保持的实现，实现使用的存储方式的选择，比如redis 还是memory 
// 或者 支付方式的选择，使用zfb 还是 vx, card 等等 工厂设计模式的实现当然其他也是可以是实现的根据场景来判定

// 练习自己可以根据场景练习